package io.nutz.demo.maker.module;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import javax.servlet.http.HttpServletResponse;

import org.nutz.ioc.impl.PropertiesProxy;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.jst.impl.JstImpl;
import org.nutz.jst.loader.impl.JstClasspathLoader;
import org.nutz.lang.Files;
import org.nutz.lang.Lang;
import org.nutz.lang.Streams;
import org.nutz.lang.Strings;
import org.nutz.lang.random.R;
import org.nutz.lang.util.Context;
import org.nutz.lang.util.Disks;
import org.nutz.lang.util.FileVisitor;
import org.nutz.lang.util.NutMap;
import org.nutz.mvc.adaptor.JsonAdaptor;
import org.nutz.mvc.annotation.AdaptBy;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;

@IocBean(create="init")
public class MakerModule {
    
    @Inject
    protected PropertiesProxy conf;
    
    protected File tmpDir;
    
    protected JstClasspathLoader loader;
    
    @At("/")
    @Ok("->:/index.html")
    public void index() {}
    
    @AdaptBy(type=JsonAdaptor.class)
    @At("/maker/make")
    @Ok("json:full")
    public NutMap make(@Param("..")NutMap params) throws IOException {
        NutMap re = new NutMap();
        String key = params.getString("finalName") + "_" + R.UU32();
        File tmpRoot = Files.createDirIfNoExists(tmpDir + "/" + key);
        String msg = build(tmpRoot, params);
        if (msg == null) {
            re.put("key", key);
            re.put("ok", true);
        }
        else {
            re.put("ok", false);
            re.put("msg", msg);
        }
        return re;
    }
    
    @At("/maker/download/?")
    @Ok("raw:zip")
    public File download(String key, HttpServletResponse resp) {
        String finalName = key.substring(0,key.lastIndexOf("_"));
        resp.setHeader("Content-Disposition", "attachment; filename=" + finalName + ".zip");
        return new File(tmpDir + "/" + key + ".zip");
    }
    
    protected String build(File tmpRoot, NutMap params) throws IOException {
        int webCount = 0;
        if (params.has("jetty") && params.getAs("jetty", NutMap.class).getBoolean("enable")) {
            webCount ++;
        }
        if (params.has("tomcat") && params.getAs("tomcat", NutMap.class).getBoolean("enable")) {
            webCount ++;
        }
        if (params.has("undertow") && params.getAs("undertow", NutMap.class).getBoolean("enable")) {
            webCount ++;
        }
        if (webCount > 1) {
            return "jetty/tomcat/undertow 只能选一个";
        }
        // 首先,生成pom.xml
        String pomStr = _render("_pom.xml", params);
        Files.write(new File(tmpRoot, "pom.xml"), pomStr);
        
        // 接下来,生成src/main里面的东西
        // 生成application.properties

        // 根据params.pout.enable 判断是否要以打包在外面的方式生成文件

        if(getEnable(params, "pout")) {
            createProperties(params, tmpRoot, "beetlsql.properties");
            createProperties(params, tmpRoot, "db.properties");
            createProperties(params, tmpRoot, "mongo.properties");
            createProperties(params, tmpRoot, "ngrok.properties");
            createProperties(params, tmpRoot, "redis.properties");
            createProperties(params, tmpRoot, "server.properties");
            createProperties(params, tmpRoot, "uflo.properties");
            createProperties(params, tmpRoot, "undertow.properties");
            createProperties(params, tmpRoot, "ureport.properties");
            createProperties(params, tmpRoot, "urule.properties");
            _write(new File(tmpRoot, "src/main/resources/application.properties"), "nutz.boot.configure.properties.dir=config");
        } else {
            String applicationPropertiesStr = _render("src/main/resources/application.properties", params);
            _write(new File(tmpRoot, "src/main/resources/application.properties"), applicationPropertiesStr);
        }
        
        // 生成log4j.properties
        String log4jPropertiesStr = _render("src/main/resources/log4j.properties", params);
        _write(new File(tmpRoot, "src/main/resources/log4j.properties"), log4jPropertiesStr);
        

        String indexHtmlStr = _render("src/main/resources/static/index.html", params);
        _write(new File(tmpRoot, "src/main/resources/static/index.html"), indexHtmlStr);
        

        String packagePath = params.getString("packageName").replace('.', '/');
        
        // 生成MainLauncher
        String _java = _render("src/main/java/_package/MainLauncher.java", params);
        _write(new File(tmpRoot, "src/main/java/"+packagePath + "/MainLauncher.java"), _java);
        
        if (params.has("nutzdao") && params.getAs("nutzdao", NutMap.class).getBoolean("enable")) {
            _java = _render("src/main/java/_package/module/UserModule.java", params);
            _write(new File(tmpRoot, "src/main/java/"+packagePath + "/module/UserModule.java"), _java);

            _java = _render("src/main/java/_package/bean/User.java", params);
            _write(new File(tmpRoot, "src/main/java/"+packagePath + "/bean/User.java"), _java);
        }
        
        // 生成TimeModule
        String timeModuleStr = _render("src/main/java/_package/module/TimeModule.java", params);
        _write(new File(tmpRoot, "src/main/java/"+packagePath + "/module/TimeModule.java"), timeModuleStr);
        
        // maven wrapper from https://github.com/takari/maven-wrapper
        String key = tmpRoot.getName();
        
        // 拷贝个简介
        _copy(key, "README.md");

        // 拷贝个gitignore
        _copy(key, ".gitignore");

        // 打包,手工
        zipIt(tmpRoot);
        return null;
    }
    
    protected void _write(File target, String value) {
        Files.write(Files.createFileIfNoExists(target), value);
    }
    
    protected void _copy(String key, String from) {
        Files.write(Files.createFileIfNoExists(new File(tmpDir, key + "/" + from)), getClass().getClassLoader().getResourceAsStream("template/" + from));
    }
    
    protected String _render(String name, NutMap params) {
        Context ctx = Lang.context();
        ctx.set("params", params);
        JstImpl jst = loader.get(name);
        return jst.render(ctx);
    }
    
    protected void zipIt(File tmpRoot) throws IOException {
        ZipOutputStream zip = new ZipOutputStream(new FileOutputStream(new File(tmpRoot.getParentFile(), tmpRoot.getName() + ".zip")));
        Disks.visitFile(tmpRoot, new FileVisitor() {
            @Override
            public void visit(File file) {
                try {
                    if (file.isDirectory())
                        return;
                    String name = file.getAbsolutePath().substring(tmpRoot.getAbsolutePath().length() + 1);
                    zip.putNextEntry(new ZipEntry(name));
                    try (FileInputStream ins = new FileInputStream(file)) {
                        Streams.write(zip, ins);
                    }
                    zip.closeEntry();
                }
                catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }, null);
        zip.finish();
        zip.flush();
        zip.close();
    }

    // 根据key获取enable属性
    protected Boolean getEnable(NutMap params, String key) {
        NutMap poutMap = (NutMap)params.get(key);
        if (poutMap == null)
            return false;
        return poutMap.getBoolean("enable");
    }
    // 生成配置文件
    protected void createProperties(NutMap params, File tmpRoot, String fileName) {
        String propertiesStr = _render("src/main/resources/config/" + fileName,  params);
        if(!Strings.isBlank(propertiesStr.trim())) _write(new File(tmpRoot, "src/main/resources/config/" + fileName), propertiesStr);
    }
    
    public void init() {
        tmpDir = new File(conf.get("nutz.maker.tmpdir", "/tmp/maker")).getAbsoluteFile();
        loader = new JstClasspathLoader();
        loader.setRoot("template/");
    }

}
